﻿//using System.Collections.Generic;
//using System.Collections.ObjectModel;
//using System.Data;
//using System.Data.Entity;
//using System.Linq;
//using NGeo.GeoNames;
//using UCosmic.Domain;
//using UCosmic.Domain.Places;
//using UCosmic.Orm;

//namespace UCosmic.Storage
//{
//    public class GeoNamesToponymStorage : IStoreGeoNamesToponyms
//    {
//        #region Construction & DI

//        public IUnitOfWork UnitOfWork { get; private set; }

//        private UCosmicContext DbContext
//        {
//            get { return UnitOfWork as UCosmicContext; }
//        }

//        private readonly IConsumeGeoNames _geoNames;
//        private readonly IManageConfigurations _config;

//        public GeoNamesToponymStorage(IUnitOfWork unitOfWork, IConsumeGeoNames geoNames, IManageConfigurations config)
//        {
//            UnitOfWork = unitOfWork;
//            _geoNames = geoNames;
//            _config = config;
//        }

//        #endregion

//        public IEnumerable<GeoNamesToponym> FindMany(GeoNameFinder finder)
//        {
//            // do not track entities returned by find methods
//            var query = DbContext.GeoNamesToponyms.AsQueryable();

//            // initialize finder
//            if (finder == null)
//                finder = new GeoNameFinder();

//            // apply context tracking
//            if (!finder.IsForInsertOrUpdate)
//                query = query.AsNoTracking();

//            // apply includes
//            var includes = finder.ToBeEagerLoaded;
//            if (includes != null && includes.Count > 0)
//                query = finder.ToBeEagerLoaded.Aggregate(query, (lastInclude, nextInclude) =>
//                    lastInclude.Include(nextInclude));

//            // apply is country
//            if (finder.IsCountry.HasValue)
//                query = (finder.IsCountry.Value)
//                    ? query.Where(t => t.AsCountry != null)
//                    : query.Where(t => t.AsCountry == null);

//            var found = query.ToList();
//            return found;
//        }

//        private ReadOnlyCollection<Country> _geoNamesCountries;

//        public GeoNamesToponym FindOne(int geoNameId)
//        {
//            // first look in the db
//            var toponym = DbContext.GeoNamesToponyms.Find(geoNameId);

//            // if not in db, add it
//            if (toponym == null)
//            {
//                // invoke geonames service
//                var geoNamesToponym = _geoNames.Get(geoNameId, _config.GeoNamesUserName);
//                if (geoNamesToponym == null) return null;

//                // convert geonames type to entity
//                toponym = geoNamesToponym.ToEntity();

//                // map parent
//                var geoNamesHierarchy = _geoNames.Hierarchy(geoNameId, _config.GeoNamesUserName, ResultStyle.Short);
//                if (geoNamesHierarchy != null && geoNamesHierarchy.Items.Count > 1)
//                {
//                    toponym.Parent = FindOne(geoNamesHierarchy.Items[geoNamesHierarchy.Items.Count - 2].GeoNameId);
//                }

//                // ensure no duplicate features or time zones are added to the db
//                toponym.Feature.Class = DbContext.GeoNamesFeatureClasses.Find(toponym.Feature.ClassCode) ?? toponym.Feature.Class;
//                toponym.Feature = DbContext.GeoNamesFeatures.Find(toponym.FeatureCode) ?? toponym.Feature;
//                toponym.TimeZone = DbContext.GeoNamesTimeZones.Find(toponym.TimeZoneId) ?? toponym.TimeZone;

//                // map country
//                _geoNamesCountries = _geoNamesCountries ?? _geoNames.Countries(_config.GeoNamesUserName);
//                var geoNamesCountry = _geoNamesCountries.SingleOrDefault(c => c.GeoNameId == geoNameId);
//                if (geoNamesCountry != null)
//                {
//                    toponym.AsCountry = geoNamesCountry.ToEntity();
//                }

//                // map ancestors
//                RefreshNodeHierarchy(toponym);

//                // add to db and save
//                DbContext.GeoNamesToponyms.Add(toponym);
//                DbContext.SaveChanges();
//            }

//            return toponym;
//        }

//        private void RefreshNodeHierarchy(GeoNamesToponym toponym)
//        {
//            toponym.Ancestors = toponym.Ancestors ?? new Collection<GeoNamesToponymNode>();
//            toponym.Offspring = toponym.Offspring ?? new Collection<GeoNamesToponymNode>();

//            toponym.Ancestors.ToList().ForEach(node =>
//                DbContext.Entry(node).State = EntityState.Deleted);

//            var separation = 1;
//            var parent = toponym.Parent;
//            while (parent != null)
//            {
//                toponym.Ancestors.Add(new GeoNamesToponymNode
//                {
//                    Ancestor = parent,
//                    Separation = separation++,
//                });
//                parent = parent.Parent;
//            }
//        }
//    }
//}